The
most common form of CGI programming handles interaction between user
input in an HTML form and a database. HTML forms consist of
several input fields, each given a key identifier, and a submit button
that sends the data in a query string. Fortunately, the built-in
Python CGI module parses this information in a class called cgi. The most useful member of this cgi class is the FieldStorage attribute. It returns the values of all the form fields in a object similar to the dictionary type.[4, 2: 276].
Notice that several key features have been introduced in this example. First, in pink, the Content-Type has been changed from text to html. Also, in pink,
the ends of five lines each have a '\'. This '\' allows statements to
wrap to a second line without affecting the indentation scheme of
Python.
In the example above, we use attributes (FieldStorage, has_key, and value) of the cgi
class and the '.' qualifier to access the data sent by the HTML
form. Since the program was relatively long, we divided the code
into separate functions, including a main() function. The program works in a recursive fashion by calling itself in the HTML POST form command. Until the form is completed, the program calls generate_form. Once all the if statement conditions are met, the data is displayed.
Login Management and Security
Retrieving
data from HTML fields is useful, but the combination of logging-in
users and providing a small level of security can really show the
benefits of CGI. In the example below, we demonstrate a basic
method of password protection. The program uses the unix_md5_crypt() function that takes a password and encryption salt as parameters. It is located in the module md5crypt.py available from sabren.com. In Code Example 7.2, the usernames and encrypted passwords are stored in the file passwords.txt.
An entered password is encrypted and tested against the first 20
letters of the encrypted password from the file (Evidently, there is a
length limit on string comparisons). Note that the sys and string modules are imported to manipulate the passwords.txt file and the strings read from the file. For our example, ab is used as the encryption salt. The correct username is Python, and the correct password is Tutorial.
Code Example 7.2:
#!/usr/bin/python
# Import the CGI, string, sys, and md5crypt modules
import cgi, string, sys, md5crypt
# Required header that tells the browser how to render the HTML.
print "Content-Type: text/html\n\n"
# Define function to generate HTML form.
def generate_form():
print "<HTML>\n"
print "<HEAD>\n"
print "\t<TITLE>Info Form</TITLE>\n"
print "</HEAD>\n"
print "<BODY BGCOLOR = white>\n"
print "\t<H3>Please, enter your username and password.</H3>\n"
print "\t<TABLE BORDER = 0>\n"
print "\t\t<FORM METHOD = post ACTION = \
\"example_7.2.cgi\">\n"
print "\t\t<TR><TH>Username:</TH><TD><INPUT TYPE = text \
NAME = \"username\"></TD><TR>\n"
print "\t\t<TR><TH>Password:</TH><TD><INPUT \
TYPE = password NAME = \"password\"></TD></TR>\n"
print "\t</TABLE>\n"
print "\t<INPUT TYPE = hidden NAME = \"action\" VALUE = \
\"display\">\n"
print "\t<INPUT TYPE = submit VALUE = \"Enter\">\n"
print "\t</FORM>\n"
print "</BODY>\n"
print "</HTML>\n"
# Define function to test the password.
def test(id, passwd):
if ((id == combo[0]) and (encrypted_pw[0:20] == combo[1][0:20])):
return "passed"
else:
return "failed"
# Define function display_page.
def display_page(result):
print "<HTML>\n"
print "<HEAD>\n"
print "\t<TITLE>Info Form</TITLE>\n"
print "</HEAD>\n"
print "<BODY BGCOLOR = white>\n"
if (result == "passed"):
print "You entered the correct combo.\n"
else:
print "You entered the incorrect combo.\n"
print "</BODY>\n"
print "</HTML>\n"
# Define main function.
def main():
form = cgi.FieldStorage()
if (form.has_key("action") and form.has_key("username") \
and form.has_key("password")):
if (form["action"].value == "display"):
result
= test(form["username"].value, form["password"].value)
display_page(result)
else:
generate_form()
# Call main function.
main()
Maintaining Sessions
Once
users have logged into your site, keeping track of their requests and
associating the correct request with the correct user poses a
challenging problem. HTML does not provide a method of retaining
state. The status of a user, whether logged in or not, must be
transferred from program to program (or recursively back to the same
program).
Assigning each user with a random session key
can fix this problem. The session key, a random string or number,
can be used to correlate users and their session data. After the
key is established at login, this user data/key pairing can be stored
in a file and accessed by subsequent CGI program requests. After
the user logs out of the site, the pairing should be erased so that
future logins are not confused with those of the past.
Additionally, for security reasons, the session key file should be kept
safely away from public view.
Code Example 7.3 adds to the previous
examples by allowing a user to remain logged in. Each time the
program executes it tests for the session key and finds its owner.
Code Example 7.3:
#!/usr/bin/python
# Import the CGI, string, sys, and md5crypt modules
import cgi, string, sys, md5crypt
# Required header that tells the browser how to render the HTML.
print "Content-Type: text/html\n\n"
# Define function to generate HTML form.
def generate_form():
print "<HTML>\n"
print "<HEAD>\n"
print "\t<TITLE>Info Form</TITLE>\n"
print "</HEAD>\n"
print "<BODY BGCOLOR = white>\n"
print "\t<H3>Please, enter your username and password.</H3>\n"
print "\t<TABLE BORDER = 0>\n"
print "\t\t<FORM METHOD = post ACTION = \
\"example_7.2.cgi\">\n"
print "\t\t<TR><TH>Username:</TH><TD><INPUT TYPE = text \
NAME = \"username\"></TD><TR>\n"
print "\t\t<TR><TH>Password:</TH><TD><INPUT \
TYPE = password NAME = \"password\"></TD></TR>\n"
print "\t</TABLE>\n"
print "\t<INPUT TYPE = hidden NAME = \"action\" VALUE = \
\"display\">\n"
print "\t<INPUT TYPE = submit VALUE = \"Enter\">\n"
print "\t</FORM>\n"
print "</BODY>\n"
print "</HTML>\n"
# Define function to test the password.
def test(id, passwd):
elif (form.has_key("action") and form.has_key("username") \
and form.has_key("password")):
if (form["action"].value == "display"):
result
= test(form["username"].value, form["password"].value)
display_page(result, form["username"].value)
else:
generate_form()
# Call main function.
main()
Correlating Multiple Sessions
While
managing a single session adds to the functionality of a site,
correlating these sessions for repeat visitors presents another problem
of user account management. The module Cookie.py offers the ability for Python CGI programs to store HTTP cookies on the clients' local machines. Cookies are persistent data files stored on the client machine through the Web browser. The Cookie.py
module writes "Set-Cookie" headers and parses the HTTP-COOKIE
environment variable. These options create a more standardized
method of dealing with client tracking and data retrieval. As a
warning, most Web browsers allow users to reject the use of cookies,
and this can break code written with dependencies on cookies.
Code Example 7.4 shows the syntax for setting and reading a simple cookie.
Code Example 7.4:
#!/usr/bin/python
# Import the cgi, os, and Cookie modules.
import cgi, os, Cookie
# Define function to set a cookie.
def set_client_Cookie():
# Create a Cookie object.
a_cookie = Cookie.SmartCookie()
# Assign the cookie a value.
a_cookie["user"] = "Python"
# Required header that tells the browser how to render the HTML.
# Required header that tells the browser how to render the HTML.
print "Content-Type: text/html\n\n"
# Print the cookie value.
print "<HTML><BODY>"
print cookie_val, "user cookie read from client.\n"
print "</BODY></HTML>\n"
# Define main function.
def main():
form = cgi.FieldStorage()
if (form.has_key("set")):
if (form["set"].value == "yes"):
read_client_Cookie()
else:
set_client_Cookie()
# Call main function.
main()
While
the HTML generation may become clearer with HTML modules and the data
storage better with databases, the above examples exhibit the core of
CGI programming.